package com.poizon.infrastructure.idem.core.annoations.aop;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.poizon.infrastructure.common.util.CustomSpelParseUtil;
import com.poizon.infrastructure.idem.core.annoations.AntiReSubmit;
import com.poizon.infrastructure.idem.core.util.IRepeatedSubmitUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * AOP/防重提交注解增强
 */
@Slf4j
@Aspect
@AllArgsConstructor
public class AntiSubmitAspect {
    private IRepeatedSubmitUtil repeatedSubmitUtil;
    /**
     * 注解拦截/防重提交
     */
    @Around("@annotation(antiReSubmit)")
    public Object handleAntiReSubmit(ProceedingJoinPoint joinPoint, AntiReSubmit antiReSubmit) {
        Assert.notNull(joinPoint);
        Assert.notNull(antiReSubmit);
        Assert.notBlank(antiReSubmit.value(), "防重key不能为空字符串");

        // 防重参数
        String spelExpression = antiReSubmit.value();
        long expireTime = antiReSubmit.expireTime();
        TimeUnit timeUnit = antiReSubmit.timeUnit();
        String failMsg = antiReSubmit.failMsg();

        // 防重键解析
        String idemKey = CustomSpelParseUtil.parseSpelValue(joinPoint, spelExpression);

        log.debug(">防重key={}", idemKey);

        // 防重执行
        AtomicReference<Object> proceed = new AtomicReference<>();
        repeatedSubmitUtil.executeRepeatedSubmit(
                idemKey,
                expireTime,
                () -> {
                    try {
                        // 业务执行
                        proceed.set(joinPoint.proceed());
                    } catch (Throwable e) {
                        // 业务执行异常
                        log.info(">防重提交业务执行失败，异常信息:{}", e);
                        throw new RuntimeException(e.getMessage());
                    }
                }, () -> {
                    // 重复执行
                        log.debug(">防重key={},重复提交", idemKey);
                    // 抛出重复提交提示
                    throw new RuntimeException(failMsg);
                }, timeUnit);

        if (log.isDebugEnabled()) {
            log.debug(">防重提交执行完毕，返回结果={}", JSON.toJSONString(proceed.get(), true));
        }

        return proceed.get();
    }

}
